package com.dmc.service.impl;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dmc.conf.PayCallBackConfig;
import com.dmc.dict.*;
import com.dmc.mapper.PaymentMapper;
import com.dmc.model.Payment;
import com.dmc.model.RestResp;
import com.dmc.model.RestRespResult;
import com.dmc.service.OrdersService;
import com.dmc.util.DecimalUtil;
import com.dmc.util.SessionUtil;
import com.dmc.util.id.IdUtil;
import com.dmc.vo.PaymentVo;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.RsaKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 第三方支付接口--微信
 */
@Service
@Transactional
public class WeChatPayServiceImpl extends AbstractPayServiceImpl {
    public static final String WEIXIN_RETURN_MESSAGE_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    public static final String WEIXIN_RETURN_MESSAGE_FAIL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>";



    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private PayCallBackConfig payCallBackConfig;

    @Autowired
    private PaymentMapper paymentMapper;

    @Autowired
    private OrdersService ordersService;


    @Override
    public RestRespResult<Map<String, String>> pay(PaymentVo payment) {
        replaceOrderIdToNo(payment);
        RestRespResult<Map<String, String>>  restResp = null;
        //获取微信商户号等信息
        String openId = payment.getOpenId();
        String payModel = payment.getPayModel();
        String payType = payment.getPayType();
        Integer orderType = payment.getOrderType();
        Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get(payType + payModel);
        Assert.notNull(wechatAppInfo,"参数错误,微信支付只支持小程序支付");
        String orderId = payment.getOrderId();
        String amount = payment.getAmount();
        Long userId = payment.getUserId();
        //根据订单号查询对应支付单
        if(OrderTypeDict.ORDER_TYPE_GOODS.getCn() == orderType){
            //如果是商品订单,则从订单获取
            BigDecimal orderFinalAmount=ordersService.getFinalAmountByOrderNo(orderId);
            amount = DecimalUtil.mul(orderFinalAmount.toString(),"100").setScale(0).toString();
        }
        Payment paymentCheckout = getPayment(userId,PaymentTypeDict.PAYMENT_TYPE_PAY.getCn(),payment.getOrderType(),orderId,null,false,new BigDecimal(amount));
        JSONObject jsonObject = payOrderExePre(paymentCheckout.getCheckoutId(),PaymentTypeDict.PAYMENT_TYPE_PAY.getCn(), orderId, orderType,amount);

        String goodsStr = jsonObject.getString("orderDetail");
        goodsStr = StringUtils.isBlank(goodsStr)?orderId:goodsStr;
        //新增还是更新
        boolean save = paymentCheckout.getId() == null;
        Long checkoutId = paymentCheckout.getCheckoutId();
        String ip = payment.getIp();
        ip = StringUtils.isBlank(ip)?"194.111.121.008":ip;
        String orderAttach = "订单支付附加数据,查询API和支付通知中原样返回";
        String appId = wechatAppInfo.get("appId");
        String mchId = wechatAppInfo.get("mchId");
        String apiKey = wechatAppInfo.get("apiKey");
        Map<String, String> result = null;
        try {
            Map<String, String> params = UnifiedOrderModel
                    .builder()
                    .appid(appId)
                    .mch_id(mchId)
                    .nonce_str(WxPayKit.generateStr())
                    .body(goodsStr)
                    .attach(orderAttach)
                    .out_trade_no(String.valueOf(checkoutId))
                    .total_fee(amount)
                    .spbill_create_ip(ip)
                    .notify_url(payCallBackConfig.getWechat())
                    .trade_type(TradeType.JSAPI.getTradeType())
                    .openid(openId)
                    .build()
                    .createSign(apiKey, SignType.MD5);
            log.info("pay ----callback:{}",payCallBackConfig.getWechat());
            String xmlResult = WxPayApi.pushOrder(false, params);
            result = WxPayKit.xmlToMap(xmlResult);
            paymentCheckout.setStartTime(new Date());
        }catch (Exception e){
            log.error("WeChatPayServiceImpl pay param:{}",ToStringBuilder.reflectionToString(payment),e);
            throw new IllegalArgumentException("调用微信支付异常");
        }
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        String tradeStatus = result.get("trade_status");
        String errCodeDes = result.get("err_code_des");
        paymentCheckout.setPaymentResult(JSON.toJSONString(result));
        if(WxPayKit.codeIsOk(returnCode)){
            String resultCode = result.get("result_code");
            if (WxPayKit.codeIsOk(resultCode)) {
                String prepayId = result.get("prepay_id");
                Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(appId, prepayId, apiKey, null);
                restResp = RestRespResult.ok("发起微信支付成功");
                restResp.setData(packageParams);
                packageParams.put("checkoutId",paymentCheckout.getCheckoutId()+"");
                paymentCheckout.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_SUCCESS.getCn());
            }else{
                paymentCheckout.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_FAIL.getCn());
                restResp = RestRespResult.error( RestResp.ERROR, errCodeDes);
            }
        }else{
            paymentCheckout.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_FAIL.getCn());
            restResp = RestRespResult.error( RestResp.ERROR, errCodeDes);
        }
        paymentCheckout.setTradeStatus(tradeStatus);
        if(save){
            paymentCheckout.setId(IdUtil.generateId());
            paymentCheckout.setCreateTime(new Date());
            paymentMapper.insert(paymentCheckout);
        }else{
            paymentMapper.updateById(paymentCheckout);
        }
        return restResp;
    }

    /**
     * 将微信回传菜蔬拼接成url参数
     * @param params
     * @return
     */
    public static final String formatParams(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        return sb.toString().substring(0, sb.length() - 1);
    }
    @Override
    public  RestRespResult<Map<String, String>>  queryPay(PaymentVo payment) {
        //获取微信商户号等信息
        String payModel = payment.getPayModel();
        String payType = payment.getPayType();
        String orderId = payment.getOrderId();
        Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get(payType + payModel);
        if(wechatAppInfo == null){
            return RestRespResult.error( RestResp.ERROR, "参数错误,微信支付只支持小程序支付");
        }
        Long userId = payment.getUserId();
        Payment paymentCheckout = getPayment(userId,PaymentTypeDict.PAYMENT_TYPE_PAY.getCn(),payment.getOrderType(),orderId,payment.getCheckoutId(),true,null);
        Long checkoutId = paymentCheckout.getCheckoutId();
        String appId = wechatAppInfo.get("appId");
        String mchId = wechatAppInfo.get("mchId");
        String apiKey = wechatAppInfo.get("apiKey");
        Map<String, String> params = OrderQueryModel.builder()
                .appid(appId)
                .mch_id(mchId)
                .out_trade_no(String.valueOf(checkoutId))
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(apiKey, SignType.MD5);
        String xmlResult = WxPayApi.orderQuery(params);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return RestRespResult.error( RestResp.ERROR, returnMsg);
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return RestRespResult.error( RestResp.ERROR, returnMsg);
        }
        RestRespResult restResp = RestRespResult.ok("查询微信支付成功");
        restResp.setData(result);
        return restResp;
    }


    @Override
    public RestRespResult<String> payCallBack(HttpServletRequest request) {
        RestRespResult restResp = null;
        String result = HttpKit.readData(request);
        Map<String, String> resultMap = WxPayKit.xmlToMap(result);
        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        String out_trade_no = resultMap.get("out_trade_no");
        boolean updateOrder = false;

        if (WxPayKit.codeIsOk(returnCode)) {
            String resultCode = resultMap.get("result_code");
            if (WxPayKit.codeIsOk(resultCode)) {
                updateOrder = true;
                restResp = RestRespResult.ok("微信回调成功");
                restResp.setData(WeChatPayServiceImpl.WEIXIN_RETURN_MESSAGE_SUCCESS);
            }else{
                restResp = RestRespResult.error(RestResp.ERROR, "微信回调失败");
                restResp.setData(WeChatPayServiceImpl.WEIXIN_RETURN_MESSAGE_FAIL);
            }
        }else{
            restResp = RestRespResult.error(RestResp.ERROR, "微信回调失败");
            restResp.setData(WeChatPayServiceImpl.WEIXIN_RETURN_MESSAGE_FAIL);
        }
        try {
            Payment paymentCheckout = paymentMapper.getByCheckoutId(Long.valueOf(out_trade_no));
            if(updateOrder && paymentCheckout.getCompleteTime()==null){
                //支付成功,更新订单支付状态
                paymentCheckout.setCompleteTime(new Date());
                String orderId = paymentCheckout.getOrderId();
                log.debug("1111支付回调时对订单相关处理payCallBackOrderExePre");
                payCallBackOrderExePre(orderId,paymentCheckout.getOrderType(), 1,DistriButionPayTypeDict.DISTRIBUTION_PAY_TYPE_1.getCn());
                paymentCheckout.setPaymentResult(JSON.toJSONString(resultMap));
                paymentMapper.updateById(paymentCheckout);
            }
        }catch (Exception e){
            log.error("WeChatPayServiceImpl payCallBack param:{}", JSON.toJSONString(resultMap),e);
        }
        return restResp;
    }


    public RestResp getPublicKey(PaymentVo payment) {
        RestResp restResp = null;
        try {
            //获取微信商户号等信息
            Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get( payment.getPayType() + payment.getPayModel());
            if(wechatAppInfo == null){
                return RestResp.error( RestResp.ERROR, "参数错误,微信支付只支持小程序支付");
            }
            String appId = wechatAppInfo.get("appId");
            String mchId = wechatAppInfo.get("mchId");
            String apiKey = wechatAppInfo.get("apiKey");
            Map<String, String> params = new HashMap<String, String>(4);
            params.put("mch_id", mchId);
            params.put("nonce_str", WxPayKit.generateStr());
            params.put("sign_type", "MD5");
            String createSign = WxPayKit.createSign(params, apiKey, SignType.MD5);
            params.put("sign", createSign);
            String result = WxPayApi.getPublicKey(params, wechatAppInfo.get("certPath"), mchId);
            Map<String, String> map = WxPayKit.xmlToMap(result);
            String returnCode = map.get("return_code");
            String returnMsg = map.get("return_msg");
            if (!WxPayKit.codeIsOk(returnCode)) {
                return RestResp.error( RestResp.ERROR, returnMsg);
            }
            String resultCode = map.get("result_code");
            if (!WxPayKit.codeIsOk(resultCode)) {
                return RestResp.error( RestResp.ERROR, returnMsg);
            }
            restResp = RestResp.ok("发起微信支付成功");
            restResp.setData(result);
        } catch (Exception e) {
            log.info("WeChatPayServiceImpl getPublicKey param:{}", ToStringBuilder.reflectionToString(payment),e);
        }
        return restResp;
    }

    @Override
    public RestRespResult<String> transfer(PaymentVo payment) {
        replaceOrderIdToNo(payment);
        RestRespResult restResp = null;
        String transfers = null;
        String openId = payment.getOpenId();
        String payModel = payment.getPayModel();
        String payType = payment.getPayType();
        Long userId = payment.getUserId();
        String orderId = payment.getOrderId();
        String amount = payment.getAmount();

        Payment paymentTransfer = getPayment(userId,PaymentTypeDict.PAYMENT_TYPE_TRANSFER.getCn(),payment.getOrderType(),orderId,null,false,new BigDecimal(amount));
        payOrderExePre(paymentTransfer.getCheckoutId(),PaymentTypeDict.PAYMENT_TYPE_TRANSFER.getCn(), orderId, OrderTypeDict.ORDER_TYPE_TRANSFER.getCn(),amount);
        try {
            String ip = payment.getIp();
            //获取微信商户号等信息
            Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get(payType + payModel);
            if(wechatAppInfo == null){
                return RestRespResult.error( RestResp.ERROR, "参数错误,微信支付只支持小程序支付");
            }
            Long checkoutId = paymentTransfer.getCheckoutId();
            String appId = wechatAppInfo.get("appId");
            String mchId = wechatAppInfo.get("mchId");
            String apiKey = wechatAppInfo.get("apiKey");
            ip = StringUtils.isBlank(ip)?"127.0.0.1":ip;
            int transferType = payment.getTransferType();
            if(TransferTypeDict.TRANSFER_TYPE_WECHAT_NUM.getCn()== transferType){
                Map<String, String> params = TransferModel.builder()
                        .mch_appid(appId)
                        .mchid(mchId)
                        .nonce_str(WxPayKit.generateStr())
                        .partner_trade_no(String.valueOf(checkoutId))
                        .openid(openId)
                        .check_name("NO_CHECK")
                        .amount(payment.getAmount())
                        .desc("提现到用户:openId")
                        .spbill_create_ip(ip)
                        .build()
                        .createSign(apiKey, SignType.MD5, false);
                transfers = WxPayApi.transfers(params, wechatAppInfo.get("certPath"), mchId);
            }else if(TransferTypeDict.TRANSFER_TYPE_BAND_NUM.getCn()== transferType){
                String publicKey = wechatAppInfo.get("publicKey");
                Map<String, String> params = new HashMap<String, String>(10);
                params.put("mch_id", mchId);
                params.put("partner_trade_no", String.valueOf(checkoutId));
                params.put("nonce_str", WxPayKit.generateStr());
                //收款方银行卡号
                params.put("enc_bank_no", RsaKit.encryptByPublicKeyByWx(payment.getBankCardNum(), publicKey));
                //收款方用户名
                params.put("enc_true_name", RsaKit.encryptByPublicKeyByWx(payment.getPayeeRealName(), publicKey));
                //收款方开户行
                params.put("bank_code", payment.getBankCode());
                params.put("amount", payment.getAmount());
                params.put("desc", "付款到银行卡");
                params.put("sign", WxPayKit.createSign(params, apiKey, SignType.HMACSHA256));
                transfers = WxPayApi.payBank(params, wechatAppInfo.get("certPath"), mchId);
            }
            log.info("WeChatPayServiceImpl transfer result:{}",transfers);
            Map<String, String> map = WxPayKit.xmlToMap(transfers);
            String returnCode = map.get("return_code");
            String returnMsg = map.get("return_msg");
            if (WxPayKit.codeIsOk(returnCode)) {
                String resultCode = map.get("result_code");
                if (WxPayKit.codeIsOk(resultCode)) {
                    //支付成功,更新订单支付状态
                    log.debug("1支付回调时对订单相关处理payCallBackOrderExePre");
                    payCallBackOrderExePre(orderId,paymentTransfer.getOrderType(),null,null);
                    paymentTransfer.setStartTime(new Date());
                    paymentTransfer.setCompleteTime(new Date());
                    restResp = RestRespResult.ok("提现成功");
                }else{
                    restResp = RestRespResult.error(RestResp.ERROR, "提现失败");
                }
            }else{
                restResp = RestRespResult.error(RestResp.ERROR, "提现失败");
            }

        }catch (Exception e){
            log.info("WeChatPayServiceImpl transfer param:{}", ToStringBuilder.reflectionToString(payment),e);
        }
        paymentTransfer.setCreateTime(new Date());
        paymentMapper.insert(paymentTransfer);
        return restResp;
    }

    @Override
    public RestRespResult<String> refund(PaymentVo payment) {
        RestRespResult restResp = null;

        String payModel = payment.getPayModel();
        String payType = payment.getPayType();
        Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get(payType + payModel);
        if(wechatAppInfo == null){
            return RestRespResult.error( RestResp.ERROR, "参数错误,微信支付只支持小程序支付");
        }
        String orderId = payment.getOrderId();
        //根据订单号查询对应支付单
        Payment paymentCheckout = getPayment(null,PaymentTypeDict.PAYMENT_TYPE_PAY.getCn(),OrderTypeDict.ORDER_TYPE_GOODS.getCn(),orderId,payment.getCheckoutId(),true,null);
        Integer orderType = paymentCheckout.getOrderType();
        //退款时,订单前置处理
        JSONObject jsonObject = refundOrderExePre(PaymentTypeDict.PAYMENT_TYPE_REFUND.getCn(), orderId, orderType);
        //校验此次退款金额,是否超过此订单之前支付的金额可退金额 TODO
        Long userId = paymentCheckout.getUserId();
        Payment paymentRefund = getPayment(userId,PaymentTypeDict.PAYMENT_TYPE_REFUND.getCn(),OrderTypeDict.ORDER_TYPE_GOODS.getCn(),orderId,null,false,new BigDecimal(payment.getAmount()));
        boolean save = paymentRefund.getId()==null;
        String appId = wechatAppInfo.get("appId");
        String mchId = wechatAppInfo.get("mchId");
        String apiKey = wechatAppInfo.get("apiKey");
        Map<String, String> params = RefundModel.builder()
                .appid(appId)
                .mch_id(mchId)
                .nonce_str(WxPayKit.generateStr())
                .out_trade_no(String.valueOf(paymentCheckout.getCheckoutId()))
                .out_refund_no(String.valueOf(paymentRefund.getCheckoutId()))
                .total_fee(payment.getAmount())
                .refund_fee(payment.getAmount())
                .notify_url(payCallBackConfig.getWechatRefund())
                .build()
                .createSign(apiKey, SignType.MD5);
        log.info("退款参数日志 result:{}"+params+"详细参数:out_trade_no:"+paymentCheckout.getCheckoutId()
                +",out_refund_no:"+paymentRefund.getCheckoutId()+",payment.getAmount()"+payment.getAmount()
                +",payCallBackConfig.getWechatRefund():"+payCallBackConfig.getWechatRefund()+",mch_id:"+mchId+",appid:"+appId);
        String refundStr = WxPayApi.orderRefund(false, params,System.getProperty("user.dir")+wechatAppInfo.get("certPath"), mchId);
        log.info("WeChatPayServiceImpl refund result:{}",refundStr);
        Map<String, String> map = WxPayKit.xmlToMap(refundStr);

        String returnCode = map.get("return_code");
        String returnMsg = map.get("return_msg");
        if(WxPayKit.codeIsOk(returnCode)){
            String resultCode = map.get("result_code");
            if (WxPayKit.codeIsOk(resultCode)) {
                restResp = RestRespResult.ok("发起退款成功");
                paymentRefund.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_SUCCESS.getCn());
                paymentRefund.setCompleteTime(new Date());
                refundBackOrderExePre(orderId,orderType,jsonObject.get("order"));
            }else{
                paymentRefund.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_FAIL.getCn());
                restResp = RestRespResult.error( RestResp.ERROR, returnMsg);
            }
        }else{
            paymentRefund.setPaymentStatus(PaymentStatusDict.PAYMENT_STATUS_FAIL.getCn());
            restResp = RestRespResult.error( RestResp.ERROR, returnMsg);
        }
        paymentRefund.setPaymentResult(JSON.toJSONString(map));
        paymentRefund.setTradeStatus(map.get("trade_status"));
        if(save){
            paymentRefund.setId(IdUtil.generateId());
            paymentRefund.setCreateTime(new Date());
            paymentMapper.insert(paymentRefund);
        }else{
            paymentMapper.updateById(paymentRefund);
        }
        return restResp;
    }

    @Override
    public RestRespResult<String> queryRefund(PaymentVo payment) {
        //根据订单编号查询退款退款详情 TODO
        Map<String, String> wechatAppInfo = AbstractPayServiceImpl.payMap.get(payment.getPayType() + payment.getPayModel());
        if(wechatAppInfo == null){
            return RestRespResult.error( RestResp.ERROR, "参数错误,微信支付只支持小程序支付");
        }
        Map<String, String> params = RefundQueryModel.builder()
                .appid(wechatAppInfo.get("appId"))
                .mch_id(wechatAppInfo.get("mchId"))
                .nonce_str(WxPayKit.generateStr())
                .out_trade_no(payment.getOrderCode())
                .build()
                .createSign(wechatAppInfo.get("apiKey"), SignType.MD5);
        String refundInfo = WxPayApi.orderRefundQuery(false, params);
        Map<String, String> map = WxPayKit.xmlToMap(refundInfo);
        String returnCode = map.get("return_code");
        String returnMsg = map.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return RestRespResult.error( RestResp.ERROR, returnMsg);
        }
        String resultCode = map.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return RestRespResult.error( RestResp.ERROR, returnMsg);
        }
        String jsonStr = JSON.toJSONString(map);
        RestRespResult restResp = RestRespResult.ok("查询支付成功");
        restResp.setData(jsonStr);
        return restResp;
    }


    @Override
    public RestResp refundCallBack(PaymentVo payment) {


        return null;
    }

}
